﻿using System;
using System.Collections.Generic;
using System.Reflection;
using Omniscient.Foundation.Contrib.Data.MySql;
using Omniscient.Foundation.Data;
using MySql.Data.MySqlClient;
using Omniscient.Foundation.ApplicationModel;

namespace Omniscient.TimeTracker.Data.Adapters.MySql
{
    public class TimeEntryAdapter : IEntityAdapter<TimeEntry>
    {
        private IConnectivity _connectivity;
        private Dictionary<string, string> _foreignKeyMap;

        public TimeEntryAdapter() : this(ApplicationManager.Current.ServiceProvider.GetService<IConnectivity>()) { }

        public TimeEntryAdapter(IConnectivity connectivity)
        {
            if (connectivity == null) throw new ArgumentNullException("The adapter cannot work with no connectivity.  Null argument: connectivity.");

            _connectivity = connectivity;

            _foreignKeyMap = new Dictionary<string, string>();
            foreach (PropertyInfo property in typeof(TimeEntry).GetProperties(BindingFlags.Public))
            {
                foreach (EntityPropertyAttribute attribute in property.GetCustomAttributes(typeof(EntityPropertyAttribute), true))
                {
                    if (attribute.Type == EntityPropertyType.Reference && attribute.ColumnName != string.Empty)
                    {
                        _foreignKeyMap.Add(property.Name, attribute.ColumnName);
                    }
                }
            }
        }

        #region IEntityAdapter<TimeEntry> Members

        public EntityList<TimeEntry> LoadAll()
        {
            throw new NotImplementedException();
        }

        public EntityList<TimeEntry> LoadByForeignKey(string propertyName, Guid entityId)
        {
            string sql;
            EntityList<TimeEntry> entities;

            if (!_foreignKeyMap.ContainsKey(propertyName))
                throw new ArgumentException(
                    string.Format("Property named {0} has not been mapped.  Properties are automatically mapped when they are decorated with the EntityPropertyAttribute attribute, their type is Reference, with a non-empty ColumnName.", propertyName));

            sql = string.Format(
                @"SELECT TimeEntryGuid, TaskGuid, ResourceGuid, CategoryGuid, StartDate, EndDate, Comments
                    FROM TimeEntry
                    WHERE {0} = '{1}'", _foreignKeyMap[propertyName], entityId.ToString("D"));

            MySqlConnection connection;
            connection = _connectivity.CreateConnection();
            try
            {
                MySqlCommand command = connection.CreateCommand();
                command.CommandText = sql;
                connection.Open();
                MySqlDataReader reader = command.ExecuteReader();

                entities = new EntityList<TimeEntry>();
                while (reader.Read())
                {
                    TimeEntry entity;
                    Guid id;

                    id = new Guid(reader.GetString("TimeEntryGuid"));
                    entity = new TimeEntry(id, true);

                    //todo: add a signature LoadByForeignKey(IEntity foreignEntity).  The mapping will be made by using
                    //the type of the entity, and it will be automatically assigned.
                    id = new Guid(reader.GetString("TaskGuid"));
                    entity.Task = new Task(id, false);

                    id = new Guid(reader.GetString("ResourceGuid"));
                    entity.Resource = new Resource(id, false);

                    id = new Guid(reader.GetString("CategoryGuid"));
                    entity.Category = new Category(id, false);

                    entity.StartDateTime = reader.GetDateTime("StartDate");
                    entity.EndDateTime = reader.GetDateTime("EndDate");
                    entity.Comments = reader.GetString("Comments");

                    entities.Add(entity);
                }
                reader.Close();
                return entities;
            }
            finally
            {
                connection.Dispose();
            }
        }

        public TimeEntry LoadByKey(Guid id)
        {
            throw new NotImplementedException();
        }

        public EntityList<TimeEntry> LoadByObjectQuery(Omniscient.Foundation.Data.ObjectQuery.OQuery<TimeEntry> query)
        {
            throw new NotImplementedException();
        }

        public EntityList<TimeEntry> LoadByQuery(string queryName)
        {
            throw new NotImplementedException();
        }

        public EntityList<TimeEntry> LoadByValueProperty(string propertyName, object value)
        {
            throw new NotImplementedException();
        }

        public void Save(IEnumerable<TimeEntry> entities)
        {
            foreach (TimeEntry entity in entities)
                Save(entity);
        }

        public void Save(TimeEntry entity)
        {
            // Cannot save entity without a valid resource
            if (entity.Resource == null) throw new ArgumentNullException("entity.Resource");

            string sql = string.Empty;
            MySqlConnection connection;

            switch (entity.Status)
            {
                case EntityStatus.Dirty:
                    sql = string.Format(
                        @"UPDATE timeentry
                        SET TaskGuid = '{1}',
                            ResourceGuid = '{2}',
                            CategoryGuid = '{3}',
                            StartDate = '{4}',
                            EndDate = '{5}',
                            Comments = '{6}'
                        WHERE
                            TimeEntryGuid = '{0}'",
                        entity.Id, entity.Task.Id, entity.Resource.Id, entity.Category.Id, entity.StartDateTime, entity.EndDateTime, entity.Comments);
                    break;
                case EntityStatus.New:
                    sql = @"INSERT INTO timeentry (TimeEntryGuid, TaskGuid, ResourceGuid, CategoryGuid, StartDate, EndDate, Comments) VALUES (?guid, ?taskId, ?resourceId, ?categoryId, ?startDate, ?endDate, ?comments)";
                    break;
                case EntityStatus.ToBeDeleted:
                    sql = string.Format("DELETE FROM timeentry WHERE TimeEntryGuid = '{0}'", entity.Id);
                    break;
                default:
                    return;
            }

            connection = _connectivity.CreateConnection();
            try
            {
                MySqlCommand command = connection.CreateCommand();
                command.Parameters.Add("?guid", MySqlDbType.VarChar).Value = entity.Id;
                command.Parameters.Add("?taskId", MySqlDbType.VarChar).Value = entity.Task.Id;
                command.Parameters.Add("?resourceId", MySqlDbType.VarChar).Value = entity.Resource.Id;
                command.Parameters.Add("?categoryId", MySqlDbType.VarChar).Value = entity.Category.Id;
                command.Parameters.Add("?startDate", MySqlDbType.DateTime).Value = entity.StartDateTime;
                command.Parameters.Add("?endDate", MySqlDbType.DateTime).Value = entity.EndDateTime;
                command.Parameters.Add("?comments", MySqlDbType.VarChar).Value = entity.Comments;
                command.CommandText = sql;
                connection.Open();
                command.ExecuteNonQuery();
            }
            finally
            {
                connection.Dispose();
            }
        }

        #endregion
    }
}
